package main

import (
	"context"
	"fmt"
	config2 "go-pan/internal/config"
	controller2 "go-pan/internal/controller"
	"go-pan/internal/log"
	"go-pan/internal/utils"
	"net/http"
	"os"
	"time"

	gin "github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"go.uber.org/zap"

	"go-pan/internal/metrics"
	"go-pan/internal/middleware"
	"go-pan/internal/repository"
	"go-pan/internal/service"
	"go-pan/internal/storage/provider"
)

func main() {
	// 初始化配置（包括日志和数据库）
	config2.Init()
	defer log.Sync()

	// 初始化JWT配置
	utils.InitJWT()

	env := config2.GetConfig()
	log.SystemLogger.Info("starting application",
		zap.String("version", "1.0.0"),
		zap.String("environment", os.Getenv("APP_ENV")),
		zap.String("mode", env.Server.Mode),
	)

	// 设置Gin模式
	gin.SetMode(env.Server.Mode)

	// 注册 Prometheus 采集器
	metrics.RegisterCollectors()

	// 使用 gin.New() 而不是 gin.Default()，避免 Gin 的内置日志干扰
	r := gin.New()

	// 添加 Recovery 中间件（gin.Default() 会自动添加，但 gin.New() 不会）
	r.Use(gin.Recovery())
	r.Use(middleware.PrometheusMiddleware())

	// 注入中间件（访问日志应该在AuthMiddleware之前，以记录所有请求）
	r.Use(middleware.AccessLogMiddleware())
	r.Use(middleware.AuthMiddleware())
	r.Use(middleware.CSRFMiddleware()) // CSRF保护（在AuthMiddleware之后，需要用户信息）

	// 初始化存储提供者
	var localProvider provider.StorageProvider
	if env.Storage.Type == "local" {
		localProvider = provider.NewLocalFileStorageProvider(env.Storage.LocalPath)
	} else {
		// 如果配置了MinIO，可以使用MinIO提供者
		// 这里暂时使用本地存储
		log.SystemLogger.Warn("unsupported storage type, using local storage",
			zap.String("configured_type", env.Storage.Type))
		localProvider = provider.NewLocalFileStorageProvider(env.Storage.LocalPath)
	}

	fileMetaRepo := repository.NewFileMetaRepository(config2.DB)
	fileChunkRepo := repository.NewFileChunkRepository(config2.DB)

	fileMetaService := service.NewFileMetaService(fileMetaRepo)
	uploadService := service.NewUploadService(
		repository.NewUploadTaskRepository(config2.DB),
		repository.NewUploadChunkRepository(config2.DB),
		fileMetaRepo, fileChunkRepo, localProvider)

	shareRepo := repository.NewShareRepository(config2.DB)
	shareService := service.NewShareService(shareRepo)
	fileHandler := controller2.FileHandler{
		FileMetaService:          fileMetaService,
		LocalFileStorageProvider: localProvider,
		ShareService:             shareService,
		FileChunkRepo:            fileChunkRepo,
	}
	uploadHandler := controller2.UploadHandler{
		UploadService: uploadService,
	}
	r.GET("/metrics", gin.WrapH(promhttp.Handler()))

	api := r.Group("/api/v1")
	{
		api.POST("/register", controller2.Register)
		api.POST("/login", controller2.Login)
		api.POST("/logout", controller2.Logout)
		api.POST("/refresh", controller2.RefreshToken) // 刷新 token 接口（不需要登录）

		auth := api.Group("/file")
		auth.Use(middleware.LoginMiddleware())
		{
			auth.POST("/upload", fileHandler.Upload)
			auth.POST("/mkdir", fileHandler.Mkdir)
			auth.POST("/rename", fileHandler.Rename)
			auth.POST("/share", fileHandler.CreateShare)
			auth.GET("/download/:id", fileHandler.Download)
			auth.DELETE("/delete/:id", fileHandler.Delete)
			auth.GET("/list", fileHandler.ListFiles)
			auth.POST("/FastUploadCheck", fileHandler.FastUploadCheck)

			auth.POST("/InitUpload", uploadHandler.InitUpload)
			auth.POST("/UploadChunk", uploadHandler.UploadChunk)
			auth.POST("/CompleteUpload/:upload_id", uploadHandler.CompleteUpload)
			auth.GET("/GetUploadStatus/:upload_id", uploadHandler.GetUploadStatus)

			// 回收站相关接口
			auth.GET("/trash", fileHandler.ListTrash)
			auth.POST("/restore/:id", fileHandler.RestoreFile)
			auth.DELETE("/hard-delete/:id", fileHandler.HardDeleteFile)

			// 文件移动接口
			auth.POST("/move", fileHandler.MoveFile)
			auth.POST("/batch-move", fileHandler.BatchMoveFiles)
		}

		// 用户信息接口（需要登录）
		userAuth := api.Group("/user")
		userAuth.Use(middleware.LoginMiddleware())
		{
			userAuth.GET("/info", controller2.GetUserInfo)
			userAuth.GET("/csrf", controller2.GetCSRFToken) // 获取 CSRF token
		}

		// Public share APIs
		api.GET("/share/:token", fileHandler.GetShareInfo)
		api.GET("/share/:token/download", fileHandler.DownloadShare)
		api.POST("/share/verify-password", fileHandler.VerifySharePassword) // 验证分享密码
	}

	// 启动定时任务：清理超过30天的回收站文件
	go startTrashCleaner(fileMetaService)

	// 使用配置的服务器地址和端口
	addr := fmt.Sprintf("%s:%d", env.Server.Host, env.Server.Port)

	// 创建 HTTP 服务器，设置超时时间以支持大文件上传
	srv := &http.Server{
		Addr:         addr,
		Handler:      r,
		ReadTimeout:  30 * time.Minute,  // 读取请求的超时时间（大文件上传）
		WriteTimeout: 30 * time.Minute,  // 写入响应的超时时间
		IdleTimeout:  120 * time.Second, // 空闲连接超时
	}

	log.SystemLogger.Info("server listening",
		zap.String("host", env.Server.Host),
		zap.Int("port", env.Server.Port),
		zap.String("address", addr),
		zap.Duration("read_timeout", srv.ReadTimeout),
		zap.Duration("write_timeout", srv.WriteTimeout))

	if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
		log.SystemLogger.Fatal("server startup failed", zap.Error(err))
	}
}

// startTrashCleaner 启动定时任务，每天清理超过30天的回收站文件
func startTrashCleaner(fileMetaService service.FileMetaService) {
	// 立即运行一次
	runTrashCleanup(fileMetaService)

	// 设置定时器：每天运行一次
	ticker := time.NewTicker(24 * time.Hour)
	defer ticker.Stop()

	for range ticker.C {
		runTrashCleanup(fileMetaService)
	}
}

// runTrashCleanup 执行回收站清理任务
func runTrashCleanup(fileMetaService service.FileMetaService) {
	log.SystemLogger.Info("starting trash cleanup task")

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
	defer cancel()

	// 查找所有超过30天的已删除文件
	files, err := fileMetaService.GetAllDeletedFilesOverDays(ctx, 30)
	if err != nil {
		log.SystemLogger.Error("trash cleanup failed to query files",
			zap.Error(err))
		return
	}

	if len(files) == 0 {
		log.SystemLogger.Info("trash cleanup completed: no files to clean")
		return
	}

	log.SystemLogger.Info("trash cleanup found files to clean",
		zap.Int("count", len(files)))

	// 批量物理删除这些文件
	deletedCount := 0
	failedCount := 0

	for _, file := range files {
		err := fileMetaService.HardDelete(ctx, file.ID)
		if err != nil {
			log.SystemLogger.Error("trash cleanup failed to hard delete file",
				zap.Int64("file_id", file.ID),
				zap.String("file_name", file.Name),
				zap.Error(err))
			failedCount++
		} else {
			deletedCount++
			log.SystemLogger.Info("trash cleanup deleted file",
				zap.Int64("file_id", file.ID),
				zap.String("file_name", file.Name),
				zap.Int64("owner_id", file.OwnerID))
		}
	}

	log.SystemLogger.Info("trash cleanup completed",
		zap.Int("total_files", len(files)),
		zap.Int("deleted_count", deletedCount),
		zap.Int("failed_count", failedCount))
}
